/**
	License:		Artistic license 2.0 (see license.txt, also available online at http://www.perlfoundation.org/artistic_license_2_0)
	Authors:		OrbitalLab (http://www.orbitallab.ru/moonglide/), 2007-2009

	File:			TextureIO.d
	Description:	texture io: DevIL wrapper - no more bicycle inventing here
	Date:			3.12.2007 by Digited
*/
module moonglide.resources.io.texture.TextureIO;

import
	moonglide.resources.io.IResIOProvider,
    tango.core.Thread;

// extra buffer size to hold header and other non-pixelmap data for DevIL save-to-memory-lump
const uint BUF_CAP = 1024;

static this()
{
	CTextureIOProvider texprov = new CTextureIOProvider();
	CResManager().RegisterProvider( texprov );
}

class CTextureThread //: Thread
{
    private:
    char[] extension;
    IResource res;
    InputStream stream;
    uint streamsize;
    IResIOProvider[ char[] ] _customProviders;		// all other/custom providers, except DevIL. Used first

    public:
    this(char[] _extension, ref IResource _res, InputStream _stream, uint _streamsize,
		IResIOProvider[ char[] ] __customProviders)
    {
        extension = _extension;
        res = _res;
        stream = _stream;
        streamsize = _streamsize;
        _customProviders = __customProviders;
        //super(&run);
    }

    void run()
    {
        	// first try custom io providers
			if( extension in _customProviders )
				return _customProviders[ extension ].Load( extension, res, stream, streamsize );

			// DevIL supports loading to lumps: bmp, dds, ico, jpg, png, tga
			ILenum type;

			switch( extension )
			{
				case "bmp": type = IL_BMP; break;
				case "dds": type = IL_DDS; break;
				case "ico": type = IL_ICO; break;
				case "jpg": type = IL_JPG; break;
				case "png": type = IL_PNG; break;
				case "tga": type = IL_TGA; break;
				default: return null;
			}

			ubyte[] buf = new ubyte[streamsize];
			uint size;

			try
			{
				size = stream.read( cast(ubyte[])buf );
			}
			catch( Exception e )
			{
				errorLog( "texture loader: could not read from stream to buffer" );
				stream.close;
				res.setNotLoaded();
				return null;
			}

			if( size != streamsize )
			{
				errorLog( "texture loader: wrong streamsize!" );
				stream.close;
				res.setNotLoaded();
				return null;
			}

			CResTextureData tex = cast(CResTextureData)res;
			moonAssert( tex !is null, "casting resource to texdata failed", __FILE__, __LINE__ );


            ilBindImage( tex.ilTextureID );

            if( !ilLoadL( type, buf.ptr, streamsize ) )
                ilDefaultImage();

			tex.UpdateInfo;
            res.setLoaded();
            stream.close;
			return tex;
    }
}

//================================================================
/// texture io provider class
//================================================================
class CTextureIOProvider : IResIOProvider
{
	protected:
        IResIOProvider[ char[] ] _customProviders;		// all other/custom providers, except DevIL. Used first
		ubyte[] savebuf;

	public:
		void Load( char[] extension, ref IResource res, InputStream stream, uint streamsize )
		in
		{
			moonAssert( extension.length > 0, "extension length == 0", __FILE__, __LINE__ );
			moonAssert( res !is null, "res is null", __FILE__, __LINE__ );
			moonAssert( stream !is null, "stream is null", __FILE__, __LINE__ );
			moonAssert( streamsize > 0, "stream size == 0", __FILE__, __LINE__ );
		}
		body
		{
		 //   res.setLoading();
		   // CTextureThread a= new CTextureThread(extension, res, stream, streamsize, _customProviders);
		    //a.start();
		   // a.run();


			// first try custom io providers
			if( extension in _customProviders )
				return _customProviders[ extension ].Load( extension, res, stream, streamsize );

			// DevIL supports loading to lumps: bmp, dds, ico, jpg, png, tga
			ILenum type;

			switch( extension )
			{
				case "bmp": type = IL_BMP; break;
				case "dds": type = IL_DDS; break;
				case "ico": type = IL_ICO; break;
				case "jpg": type = IL_JPG; break;
				case "png": type = IL_PNG; break;
				case "tga": type = IL_TGA; break;
				default: return null;
			}

			ubyte[] buf = new ubyte[streamsize];
			uint size;

			try
			{
				size = stream.read( cast(ubyte[])buf );
			}
			catch( Exception e )
			{
				errorLog( "texture loader: could not read from stream to buffer" );
				return null;
			}

			if( size != streamsize )
			{
				errorLog( "texture loader: wrong streamsize!" );
				return null;
			}

			CResTextureData tex = cast(CResTextureData)res;
			moonAssert( tex !is null, "casting resource to texdata failed", __FILE__, __LINE__ );


            ilBindImage( tex.ilTextureID );

            if( !ilLoadL( type, buf.ptr, streamsize ) )
                ilDefaultImage();

			tex.UpdateInfo;

			return tex;

		}

		//================================================================
		/// save texture to memory mapped stream with given format (extension)
		//================================================================
		bool Save( char[] format, IResource res, ref Array buf )
		in
		{
			moonAssert( format.length > 0, "format is empty!", __FILE__, __LINE__ );
			moonAssert( res !is null, "res is null!", __FILE__, __LINE__ );
			moonAssert( buf !is null, "buffer is null!", __FILE__, __LINE__ );
			moonAssert( cast(CResTextureData)res !is null, "res is not CResTextureData!", __FILE__, __LINE__ );
		}
		body
		{
			CResTextureData tex = cast(CResTextureData)res;

			if( !tex )
			{
				errorLog( "Texture io: could not cast res" );
				return false;
			}

			// first try custom io providers
			if(( format in _customProviders ) != null )
				return _customProviders[ format ].Save( format, res, buf );

			// DevIL supports saving from lumps: tga, bmp, dds
			ILenum type;

			switch( format )
			{
				case "bmp": type = IL_BMP; break;
				case "dds": type = IL_DDS; break;
				case "ico": type = IL_ICO; break;
				case "jpg": type = IL_JPG; break;
				case "png": type = IL_PNG; break;
				case "tga": type = IL_TGA; break;
				default: return false;
			}

			ilBindImage( tex.ilTextureID );
			savebuf.length = tex.bpp * tex.width * tex.height + BUF_CAP;

			uint size = ilSaveL( type, savebuf.ptr, savebuf.length );

			savebuf.length = size;
			buf.append( savebuf );

			return true;
		}

		void CreateErrorResourceData( IResource res, char[] errorResName )
		{
			// gen data
		}

		//================================================================
		/// other stuff
		//================================================================
		char[] GetResourceClass() { return CResTextureData.classinfo.name; }

		this()
		{
			DerelictIL.load();
			DerelictILU.load();
			ilInit();
			iluInit();

			ilDisable( IL_ORIGIN_SET );
			ilOriginFunc(IL_ORIGIN_UPPER_LEFT);

			ilClearColor(0.2,0.2,0.2,1);
		}

		~this()
		{
			delete savebuf;
		}
}
